import webapp2
import json
import random

from google.appengine.ext import ndb
from google.appengine.ext.ndb import polymodel
from google.appengine.api import users
from google.appengine.api import urlfetch

DEFAULT_USERGROUP = "theworsteggbert"
GAME_STATUS_PENDING = 0
GAME_STATUS_ACTIVE = 1
GAME_STATUS_COMPLETE = 2
API_KEY = "key=AIzaSyBZZxt2MJIgRh02i6AZsHVdcwPovjEZTF8"
GCM_URL = "https://android.googleapis.com/gcm/send"


#Usergroup key
def usergroup_key(usergroup_name=DEFAULT_USERGROUP):
  return ndb.Key("Usergroup", usergroup_name)

def verifyKeys(keys):
  verified = True
  for key in keys:
    if(len(key)==0):
      verified = False
  return verified

#Local User information
class LocalUser(ndb.Model):
  userId = ndb.StringProperty()
  username = ndb.StringProperty()
  invitations = ndb.KeyProperty(repeated=True)
  registrationId = ndb.StringProperty()

#Player information.  Contains relevant information for a user in a particular game (ancestor=gameKey).
class Player(ndb.Model):
  userKey = ndb.KeyProperty()#Key of the user who is currently this player
  hand = ndb.KeyProperty(repeated=True)#List of all the white card keys in this player's hand
  score = ndb.IntegerProperty()#current score of this player
  submission = ndb.KeyProperty(repeated=True)#List of all white card keys this player submitted
  position = ndb.IntegerProperty()#Table position for this player
  alert = ndb.IntegerProperty()
  
#Previous round information.  Contains information for one round of the ancestor game.
class PastRound(ndb.Model):
  roundNumber = ndb.IntegerProperty()#Round number for this round
  blackCardId = ndb.IntegerProperty()
  czar = ndb.StringProperty()#key of User who was czar in this round
  winner = ndb.StringProperty()#key of User who won this round
  playerSubmissions = ndb.JsonProperty()#Json object of the type [{'username':value, 'submission':[submitted card ids]}]
  
#Game information.  Contains information for a given game.
class Game(ndb.Model):
  gameName = ndb.StringProperty()#Game name
  status = ndb.IntegerProperty()#Game status - defined above
  host = ndb.KeyProperty()#key of User who is hosting this game
  czar = ndb.KeyProperty()#key of Player who is currently picking
  maxPlayers = ndb.IntegerProperty()#Maximum number of players who can be in this game
  currentRound = ndb.IntegerProperty()#current round number
  currentBlack = ndb.KeyProperty()#key of current black card
  subsIn = ndb.IntegerProperty()#number of submissions turned in so far
  whiteDeck = ndb.KeyProperty(repeated=True)#Keys of all white cards left in the deck
  blackDeck = ndb.KeyProperty(repeated=True)#Keys of all black cards left in the deck
  history = ndb.KeyProperty(repeated=True)#Keys of all previous rounds
  
class WhiteCard(ndb.Model):
  idNum = ndb.IntegerProperty()
  text = ndb.StringProperty()
  cardSetId = ndb.IntegerProperty()
  
class BlackCard(ndb.Model):
  idNum = ndb.IntegerProperty()
  text = ndb.StringProperty()
  draw = ndb.IntegerProperty()
  pick = ndb.IntegerProperty()
  cardSetId = ndb.IntegerProperty()

class CardSet(ndb.Model):
  idNum = ndb.IntegerProperty()
  name = ndb.StringProperty()
  description = ndb.StringProperty()
  

class Main(webapp2.RequestHandler):
    
  def get(self):
    self.response.headers['Content-Type'] = 'application/json'
    user = users.get_current_user()
    
    response = {}
    response["tag"] = "createUserOrLogin"
    if(user):
      response["success"] = 1
      response["value"] = Main.createUserOrLogin(self, user).urlsafe()
    else:
      response["error"] = 1
      
    self.response.write(json.dumps(response))
    
      
  
  def post(self):
    self.response.headers['Content-Type'] = 'application/json'
    tag = self.request.get("tag")
    
    if(len(tag)==0):
      self.response.write("Cards Against Humanity API")
    else:
      response = {}
      response["tag"] = tag
      
      if(tag=="getUsername"):
	userKey = self.request.get("userKey")
	keys = [userKey]
	if(verifyKeys(keys)):
	  username = Main.getUsername(self, ndb.Key(urlsafe=userKey))
	  if(username):
	    response["success"] = 1
	    response["value"] = username
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="createGame"):
	hostKey = self.request.get("userKey")
	maxPlayers = self.request.get("maxPlayers")
	gameName = self.request.get("gameName")
	keys = [hostKey, maxPlayers, gameName]
	if(verifyKeys(keys)):
	  gameKey = Main.createGame(self, maxPlayers, gameName, ndb.Key(urlsafe=hostKey))
	  if(gameKey):
	    response["success"] = 1
	    response["value"] = gameKey.urlsafe()
	  else:
	    respons["success"] = 0
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="cancelGame"):
	userKey = self.request.get("userKey")
	gameKey = self.request.get("gameKey")
	keys = [userKey, gameKey]
	if(verifyKeys(keys)):
	  result = Main.cancelGame(self, ndb.Key(urlsafe=gameKey), ndb.Key(urlsafe=userKey))
	  if(result):
	    response["success"] = 1
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="addPlayerToGame"):
	userKey = self.request.get("userKey")
	gameKey = self.request.get("gameKey")
	keys = [userKey, gameKey]
	if(verifyKeys(keys)):
	  playerKey = Main.addPlayerToGame(self, ndb.Key(urlsafe=userKey), ndb.Key(urlsafe=gameKey))
	  if(playerKey):
	    response["success"] = 1
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="removePlayerFromGame"):
	userKey = self.request.get("userKey")
	gameKey = self.request.get("gameKey")
	keys = [userKey, gameKey]
	if(verifyKeys(keys)):
	  result = Main.removePlayerFromGame(self, ndb.Key(urlsafe=userKey), ndb.Key(urlsafe=gameKey))
	  if(result):
	    response["success"] = 1
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="startGame"):
	hostKey = self.request.get("userKey")
	gameKey = self.request.get("gameKey")
	keys = [hostKey, gameKey]
	if(verifyKeys(keys)):
	  result = Main.startGame(self, ndb.Key(urlsafe=hostKey), ndb.Key(urlsafe=gameKey))
	  if(result):
	    response["success"] = 1
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="setAlertToSeen"):
	userKey = self.request.get("userKey")
	gameKey = self.request.get("gameKey")
	keys = [userKey, gameKey]
	if(verifyKeys(keys)):
	  result = Main.setAlertToSeen(self, ndb.Key(urlsafe=userKey), ndb.Key(urlsafe=gameKey))
	  if(result):
	    response["success"] = 1
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="submitWhite"):
	whiteCards = self.request.get("whiteCards")
	gameKey = self.request.get("gameKey")
	userKey = self.request.get("userKey")
	keys = [whiteCards, gameKey, userKey]
	if(verifyKeys(keys)):
	  result = Main.submitWhite(self, json.loads(whiteCards), ndb.Key(urlsafe=gameKey), ndb.Key(urlsafe=userKey))
	  if(result):
	    response["success"] = 1
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="submitBlack"):
	winnerKey = self.request.get("winnerKey")
	gameKey = self.request.get("gameKey")
	userKey = self.request.get("userKey")
	keys = [winnerKey, gameKey, userKey]
	if(verifyKeys(keys)):
	  result = Main.submitBlack(self, ndb.Key(urlsafe=winnerKey), ndb.Key(urlsafe=gameKey), ndb.Key(urlsafe=userKey))
	  if(result):
	    response["success"] = 1
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="endGame"):
	gameKey = self.request.get("gameKey")
	keys = [gameKey]
	if(verifyKeys(keys)):
	  result = Main.endGame(self, ndb.Key(urlsafe=gameKey))
	  if(result):
	    response["success"] = 1
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="fetchCurrentGamesJSON"):
	userKey = self.request.get("userKey")
	keys = [userKey]
	if(verifyKeys(keys)):
	  response["success"] = 1
	  response["value"] = Main.fetchCurrentGamesJSON(self, ndb.Key(urlsafe=userKey))
	else:
	  response["error"] = 2
      elif(tag=="fetchPendingGamesJSON"):
	response["success"] = 1
	response["value"] = Main.fetchPendingGamesJSON(self)
      elif(tag=="fetchPendingGameInfoJSON"):
	gameKey = self.request.get("gameKey")
	keys = [gameKey]
	if(verifyKeys(keys)):
	  response["success"] = 1
	  response["value"] = Main.fetchPendingGameInfoJSON(self, ndb.Key(urlsafe=gameKey))
	else:
	  response["error"] = 2
      elif(tag=="fetchGameInfoJSON"):
	userKey = self.request.get("userKey")
	gameKey = self.request.get("gameKey")
	keys = [userKey, gameKey]
	if(verifyKeys(keys)):
	  response["success"] = 1
	  response["value"] = Main.fetchGameInfoJSON(self, ndb.Key(urlsafe=userKey), ndb.Key(urlsafe=gameKey))
	else:
	  response["error"] = 2
      elif(tag=="fetchSubmissionsJSON"):
	gameKey = self.request.get("gameKey")
	keys = [gameKey]
	if(verifyKeys(keys)):
	  response["success"] = 1
	  response["value"] = Main.fetchSubmissionsJSON(self, ndb.Key(urlsafe=gameKey))
	else:
	  response["error"] = 2
      elif(tag=="fetchRoundHistoryJSON"):
	gameKey = self.request.get("gameKey")
	roundNumber = self.request.get("roundNumber")
	keys = [gameKey, roundNumber]
	if(verifyKeys(keys)):
	  response["success"] = 1
	  response["value"] = Main.fetchRoundHistoryJSON(self, ndb.Key(urlsafe=gameKey), int(roundNumber))
	else:
	  response["error"] = 2
      elif(tag=="fetchHistoryJSON"):
	gameKey = self.request.get("gameKey")
	keys = [gameKey]
	if(verifyKeys(keys)):
	  response["success"] = 1
	  response["value"] = Main.fetchHistoryJSON(self, ndb.Key(urlsafe=gameKey))
	else:
	  response["error"] = 2
      elif(tag=="sendAlertNotification"):
	userKeys = self.request.get("userKeys")
	gameKey = self.request.get("gameKey")
	keys = [userKeys, gameKey]
	if(verifyKeys(keys)):
	  notificationSent = Main.sendAlertNotification(self, [ndb.Key(urlsafe=userKey) for userKey in json.loads(userKeys)], ndb.Key(urlsafe=gameKey))
	  if(notificationSent):
	    response["success"] = 1
	    response["value"] = notificationSent
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      elif(tag=="fetchRegistrationId"):
	userKey = self.request.get("userKey")
	keys = [userKey]
	if(verifyKeys(keys)):
	  regId = Main.fetchRegistrationId(self, ndb.Key(urlsafe=userKey))
	  if(regId):
	    response["success"] = 1
	    response["value"] = regId
	  else:
	    response["success"] = 0
	    response["error"] = 1
	else:
	  response["success"] = 0
	  response["error"] = 2
      elif(tag=="registerUserDevice"):
	userKey = self.request.get("userKey")
	regId = self.request.get("regId")
	keys = [userKey, regId]
	if(verifyKeys(keys)):
	  result = Main.registerUserDevice(self, ndb.Key(urlsafe=userKey), regId)
	  if(result):
	    response["success"] = 1
	  else:
	    response["error"] = 1
	else:
	  response["error"] = 2
      else:
	response["error"] = 3
      
      self.response.write(json.dumps(response))
    
  #Create a new local user, or login
  #Input: username
  #Output: return new user key if does not exist, return existing userkey if already exists
  def createUserOrLogin(self, user):
    userId = user.user_id()
    usergroup_name = self.request.get('usergroup_name', DEFAULT_USERGROUP)
    user_query = LocalUser.query(LocalUser.userId==userId, ancestor=usergroup_key(usergroup_name))
    userKey = user_query.get(keys_only=True)
    if(userKey):
      return userKey
    
    newUser = LocalUser(parent = usergroup_key(usergroup_name))
    newUser.userId = userId
    newUser.username = user.nickname()
    newUser.currentGames = []
    newUser.invitations = []
    
    return newUser.put()
  
  #Return the key for a given user
  #Input: userId
  #Output: key for the given user
  def getUserKey(self, userId):
    usergroup_name = self.request.get('usergroup_name', DEFAULT_USERGROUP)
    return LocalUser.query(LocalUser.userId==userId).get(keys_only=True)
  
  #Return the username for a given userKey
  #Input: userKey
  #Output: username associated with that key
  def getUsername(self, userKey):
    return userKey.get().username
  
  
  #Create a new game
  #Input: maxPlayers, gameName, hostKey
  #Output: new game key
  def createGame(self, maxPlayers, gameName, hostKey):
    newGame = Game()
    newGame.gameName = gameName
    newGame.status = 0
    newGame.host = hostKey
    newGame.maxPlayers = int(maxPlayers)
    newGame.currentRound = 1
    newGame.subsIn = 0
    newGame.blackDeck = BlackCard.query().fetch(keys_only=True)
    newGame.whiteDeck = WhiteCard.query().fetch(keys_only=True)
    
    gameKey = newGame.put()
    Main.addPlayerToGame(self, hostKey, gameKey)
    
    return gameKey
    
  #Cancel a pending game
  #Input: hostKey, gameKey
  #Output: true if successful, false if game dne or host is invalid
  def cancelGame(self, gameKey, hostKey):
    game = gameKey.get()
    
    if(game):
      gameHostKey = game.host
      if(gameHostKey != hostKey):
	return False
      
      query = Player.query(ancestor=gameKey)
      ndb.delete_multi(query.fetch(keys_only=True))
      
      gameKey.delete()
      return True
    
    else:
      return False
    
  #Add a player to a pending game
  #Input: userKey, gameKey
  #Output: playerKey if successful, false if there is already a player with that userKey in game, game is not pending or game is full
  def addPlayerToGame(self, userKey, gameKey):
    query = Player.query(Player.userKey==userKey, ancestor=gameKey)
    if(query.get()):
      return False
    game = gameKey.get()
    
    playerQuery = Player.query(ancestor=gameKey)
    
    if((game.status != 0) or (playerQuery.count() == game.maxPlayers)):
      return False
    
    player = Player(parent=gameKey)
    player.userKey = userKey
    player.score = 0
    player.alert = 0
    
    return player.put()
  
  #Remove a player from a pending game
  #Input: userKey, gameKey
  #Output: true if successful, false if player is not in game, player is the host, or game is not pending
  def removePlayerFromGame(self, userKey, gameKey):
    query = Player.query(Player.userKey==userKey, ancestor=gameKey)
    playerKey = query.get(keys_only=True)
    if(playerKey==None):
      return False
    
    game = gameKey.get()
    
    if((game.status != 0) or (game.host == userKey)):
      return False
    
    playerKey.delete()
    return True
    
  #Fetch all non-completed games that a user is currently in
  #Input: userKey
  #Output: list of all current games
  def fetchCurrentGames(self, userKey):
    query = Player.query(Player.userKey==userKey)
    currentGames = []
    
    for playerKey in query.run(keys_only=True):
      gameKey = playerKey.parent()
      if(game.status != 2):
	currentGames += [gameKey]
    
    return currentGames
  
  #Draw n white cards for a given player
  #Input: playerKey, n
  #Output: true if successful, false if there are less than n white cards left in the deck
  def drawWhites(self, playerKey, n):
    game = playerKey.parent().get()
    whiteDeck = game.whiteDeck
    
    if(len(whiteDeck) < n):
      return False
    
    player = playerKey.get()
    hand = player.hand
    
    for i in range(n):
      hand += [whiteDeck.pop()]
      
    game.whiteDeck = whiteDeck
    game.put()
    
    player.hand = hand
    player.put()
    
    return True
  
  #Set alert to seen - change an alert to a 2 for a given player
  #Input: userKey, gameKey
  #Output: true if successful, false if game is not active, player is not in game, alert does not equal 1
  def setAlertToSeen(self, userKey, gameKey):
    game = gameKey.get()
    if((game is None) or (game.status!=1)):
      return False
    
    playerQuery = Player.query(Player.userKey==userKey, ancestor=gameKey)
    player = playerQuery.get()
    if((player is None) or (player.alert != 1)):
      return False
    
    player.alert = 2
    player.put()
    
    return True
  
  #Start a game
  #Input: hostKey, gameKey
  #Output: true if successful, false if user!=host, game is not full, or the game is not pending
  def startGame(self, hostKey, gameKey):
    game = gameKey.get()
    if(game is None):
      return False
    
    playerQuery = Player.query(ancestor=gameKey)
    
    if((game.host != hostKey) or (game.status != 0) or (game.maxPlayers != playerQuery.count())):
      
      return False
    
    #Initialize variables
    blackDeck = game.blackDeck
    whiteDeck = game.whiteDeck
    random.shuffle(blackDeck)
    random.shuffle(whiteDeck)
    game.currentBlack = blackDeck.pop()
    game.blackDeck = blackDeck
    game.whiteDeck = whiteDeck
    game.status = 1
    
    draw = 10 + game.currentBlack.get().draw
    
    positions = range(game.maxPlayers)
    random.shuffle(positions)
    
    i=0
    playerKeys = playerQuery.fetch(keys_only=True)
    userAlertKeys = [playerKey.get().userKey for playerKey in playerKeys]
    userAlertKeys.remove(hostKey)
    
    for playerKey in playerKeys:
      player = playerKey.get()
      player.position = positions[i]
      player.alert = 1
      Main.drawWhites(self, playerKey, draw)
      if(i==0):
	game.czar = playerKey
	player.alert = 0
      i+=1
      player.put()
    
    game.put()
    
    Main.sendAlertNotification(self, userAlertKeys, gameKey)
    
    return True
  
  #Submit white cards
  #Input: whiteCards, gameKey, userKey
  #Output: true if successful, false if user is not in game, user already submitted, user is current czar, game is not active, wrong number of white cards are submitted, player does not actually have cards
  def submitWhite(self, whiteCards, gameKey, userKey):
    if(len(whiteCards)==0):
      return False
    
    game = gameKey.get()
    if((game is None) or (game.status!=1)):
      return False
    
    currentBlack = game.currentBlack.get()
    
    if(currentBlack.pick != len(whiteCards)):
      return False
    
    playerQuery = Player.query(Player.userKey==userKey, ancestor=gameKey)
    if(playerQuery.count() == 0):
      
      return False
    
    playerKey = playerQuery.get(keys_only=True)
    
    if(playerKey == game.czar):
      return False

    player = playerKey.get()
    
    if(len(player.submission) != 0):
      return False
    
    whiteCardQuery = WhiteCard.query(WhiteCard.idNum.IN(whiteCards))
    whiteCardKeys = whiteCardQuery.fetch(keys_only=True)
    
    if(not set(whiteCardKeys).issubset(player.hand)):
      return False
    
    player.submission = whiteCardKeys
    hand = player.hand
    player.hand = list(set(hand) - set(whiteCardKeys))
    player.alert = 0
    player.put()
    
    game.subsIn = game.subsIn + 1
    game.put()
    
    subsNeeded = game.maxPlayers - game.subsIn - 1
    if(subsNeeded == 0):
      czarPlayer = game.czar.get()
      czarPlayer.alert = 1
      czarPlayer.put()
      Main.sendAlertNotification(self, [czarPlayer.userKey], gameKey)
    
    
    draw = 10 - len(player.hand)
    
    Main.drawWhites(self, playerKey, draw)
    
    return True
  
  #Submit a black card
  #Input: winnerKey, gameKey, userKey
  #Output: true if successful, false if user is not in game, user is not czar, user is not in game, winner is not in game, game is not active, not everyone has submitted a card
  def submitBlack(self, winnerKey, gameKey, userKey):
    game = gameKey.get()
    if((game is None) or (game.status!=1) or (game.czar.get().userKey!=userKey)):
      return False
    
    
    playerQuery = Player.query(ancestor=gameKey)
    playerKeys = playerQuery.fetch(keys_only=True)
    
    if(winnerKey not in playerKeys):
      return False
    
    currentCzarPosition = game.czar.get().position
    newCzarPosition = currentCzarPosition + 1
    if(newCzarPosition == game.maxPlayers):
      newCzarPosition = 0
    
    submissions = []
    players = []
    for playerKey in playerKeys:
      player = playerKey.get()
      players += [player]
      
      if(player.userKey == userKey):
	continue
      
      if(len(player.submission) == 0):
	
	self.response.write(player.submission)
	return False
      
      cardIds = []
      for card in player.submission:
	cardIds += [card.get().idNum]
	
      submission = {"username":player.userKey.get().username, "submission":cardIds}
      submissions += [submission]
    
    winner = winnerKey.get()
    winner.score = winner.score + 1
    winner.put()
    
    winnerName = winner.userKey.get().username
    oldRound = game.currentRound
    oldBlack = game.currentBlack.get().idNum
    czarName = game.czar.get().userKey.get().username
    
    blackDeck = game.blackDeck
    game.currentBlack = blackDeck.pop()
    game.blackDeck = blackDeck
    game.currentRound = game.currentRound + 1
    
    extraDraw = game.currentBlack.get().draw
    
    for playerKey in playerKeys:
      player = playerKey.get()
      player.submission = []
      
      if(player.position == newCzarPosition):
	game.czar = playerKey
	player.alert = 0
      else:
	player.alert = 1
      
      player.put()
      
      if(extraDraw > 0):
	Main.drawWhites(self, playerKey, extraDraw)
      
    game.subsIn = 0
    game.put()
    
    userAlertKeys = [playerKey.get().userKey for playerKey in playerKeys]
    userAlertKeys.remove(game.czar.get().userKey)
    userAlertKeys.remove(userKey)
    
    Main.sendAlertNotification(self, userAlertKeys, gameKey)
    
    pastRound = PastRound(parent=gameKey)
    pastRound.roundNumber = oldRound
    pastRound.czar = czarName
    pastRound.winner = winnerName
    pastRound.playerSubmissions = json.dumps(submissions)
    pastRound.blackCardId = oldBlack
    pastRound.put()
    
    return True
  
  #End game
  #Input: gameKey
  #Output: True if successful, false if game dne or game is not active
  def endGame(self, gameKey):
    game = gameKey.get()
    if((game is None) or (game.status!=1)):
      return False
    
    playerQuery = Player.query(ancestor=gameKey)
    playerKeys = playerQuery.fetch(keys_only=True)
    
    ndb.delete_multi(playerKeys)
    
    game.status = 2
    game.put()
    
    return True
  
  #Fetch current games as JSONObject
  #Input: userKey
  #Output: [{gameKey: name:, status:}]
  def fetchCurrentGamesJSON(self, userKey):
    playerQuery = Player.query(Player.userKey==userKey)
    playerKeys = playerQuery.fetch(keys_only=True)
    
    result = []
    for playerKey in playerKeys:
      gameKey = playerKey.parent()
      game = gameKey.get()
      result += [{"gameKey":gameKey.urlsafe(), "name":game.gameName, "status":game.status, "alert":playerKey.get().alert}]
    
    return result
  
  #Fetch all pending games as JSONObject
  #Input: none
  #Output: [{gameKey:, gameName:}]
  def fetchPendingGamesJSON(self):
    gameQuery = Game.query(Game.status==0)
    gameKeys = gameQuery.fetch(keys_only=True)
    
    result = []
    for gameKey in gameKeys:
      result += [{"gameKey":gameKey.urlsafe(), "gameName":gameKey.get().gameName}]
    
    return result
  
  #Fetch all players, maxPlayers, gameName, and hostId for a game with a given gameId (pending probably)
  #Input: gameKey
  #Return {"hostKey":, "maxPlayers":, "gameName":, "players":[{playerKey:, playerName:}]}
  def fetchPendingGameInfoJSON(self, gameKey):
    game = gameKey.get()
    if(game is None):
      return {}
    
    playerQuery = Player.query(ancestor=gameKey)
    playerKeys = playerQuery.fetch(keys_only=True)
    
    playerResults = []
    for playerKey in playerKeys:
      playerResults += [{"playerName":playerKey.get().userKey.get().username, "playerKey":playerKey.get().userKey.urlsafe()}]
      
    return {"hostKey":game.host.urlsafe(), "gameName":game.gameName, "maxPlayers":game.maxPlayers, "players":playerResults}
  
  #Fetch all relevant information about a current game with a given gameId and userId
  #Input: gameKey, userKey
  #Return: {"currentRound":, "czarHandle":, "userIsCzar":, "userSubmitted":, "subsNeeded":, "blackId":, "gameName":, "hand":[]}.  Empty if user is not in game or status is 0 or 2.
  def fetchGameInfoJSON(self, userKey, gameKey):
    playerQuery = Player.query(Player.userKey==userKey, ancestor=gameKey)
    player = playerQuery.get()
    if(player is None):
      return {}
    
    game = gameKey.get()
    if((game is None) or (game.status != 1)):
      return {}
    
    userIsCzar = False
    if(userKey == game.czar.get().userKey):
      userIsCzar = True
    
    userSubmitted = False
    if(len(player.submission) > 0):
      userSubmitted = True
    
    currentRound = game.currentRound
    czarHandle = game.czar.get().userKey.get().username
    gameName = game.gameName
    subsNeeded = game.maxPlayers - game.subsIn - 1
    blackId = game.currentBlack.get().idNum
    hand = [cardKey.get().idNum for cardKey in player.hand]
    
    return {"gameName":gameName, "currentRound":currentRound, "czarHandle":czarHandle, "userIsCzar":userIsCzar, "userSubmitted":userSubmitted, "subsNeeded":subsNeeded, "blackId":blackId, "hand":hand}
  
  #Fetch all submissions for the given game
  #Input: gameKey
  #Return: [{playerKey:, submissions:[]}]
  def fetchSubmissionsJSON(self, gameKey):
    playerQuery = Player.query(Player._key!=gameKey.get().czar, ancestor=gameKey)
    playerKeys = playerQuery.fetch(keys_only=True)
    
    return [{"playerKey":playerKey.urlsafe(), "submission":[cardKey.get().idNum for cardKey in playerKey.get().submission]} for playerKey in playerKeys]
    
  #Fetch a single round's history for a given game
  #Input: gameKey, round number
  #Output: {roundNumber:, blackCard:id, czar:, submissions:[{username:, submission:[]}], winner:}
  def fetchRoundHistoryJSON(self, gameKey, roundNumber):
    pastRoundQuery = PastRound.query(PastRound.roundNumber==roundNumber, ancestor=gameKey)
    if(pastRoundQuery.count()==0):
      return {}
    pastRound = pastRoundQuery.get()
    
    return {"roundNumber":roundNumber, "blackCardId":pastRound.blackCardId, "czar":pastRound.czar, "submissions":json.loads(pastRound.playerSubmissions), "winner": pastRound.winner}
  
  #Fetch entire history for a given game
  #Input: gameKey
  #Output: [{roundNumber:, blackCard:id, czar:, submissions:[{username:, submission:[]}], winner:}]
  def fetchHistoryJSON(self, gameKey):
    pastRoundQuery = PastRound.query(ancestor=gameKey)
    if(pastRoundQuery.count()==0):
      return {}
    pastRounds = pastRoundQuery.fetch()
    
    return [{"roundNumber":pastRound.roundNumber, "blackCardId":pastRound.blackCardId, "czar":pastRound.czar, "submissions":json.loads(pastRound.playerSubmissions), "winner": pastRound.winner} for pastRound in pastRounds]
  
  #Sends a push notification to the mobile device of the selected users alerting them that the game is ready for them to go
  #Input: userKeys, gameKey
  #Output: True if messages were sent successfully, false otherwise
  def sendAlertNotification(self, userKeys, gameKey):
    headers = {'Content-Type': 'application/json', 'Authorization': API_KEY}
    message = {}
    regIds = [userKey.get().registrationId for userKey in userKeys]
    
    #Filter out None values
    newRegIds = []
    
    for regId in regIds:
      if(regId):
	newRegIds += [regId]
    
    regIds = newRegIds
    
    message["registration_ids"] = regIds
    message["collapse_key"] = "cah"
    
    gameName = gameKey.get().gameName
    data = {}
    data["gameName"] = gameName
    data["type"] = "alert"
    message["data"] = data
    
    result = urlfetch.fetch(url=GCM_URL, payload=json.dumps(message), method=urlfetch.POST, headers=headers)
    return result.status_code
    
  #Fetch a user's registration ID
  #Input: userKey
  #Output: regId if exists, false otherwise
  def fetchRegistrationId(self, userKey):
    user = userKey.get()
    if(user is None):
      return False
    
    regId = user.registrationId
    if(regId is None):
      return False
    
    return regId
  
  #Register device to a user
  #Input: userKey, regId
  #Output: True if successful, false if user does not exist
  def registerUserDevice(self, userKey, regId):
    user = userKey.get()
    if(user is None):
      return False
    
    user.registrationId = regId
    user.put()
    
    return True

application = webapp2.WSGIApplication([
    ('/', Main),
], debug=True)